<?php

/**
 *		//模板编译类//
 *		//这是一份试验品，由大街编写（zhongmingzi@gmail.com）
 *		//本程序默认环境：Zend Optimizer Yes / 3.3.3
 *		//		Apache/2.0.63 (Win32) PHP/5.2.12
 *		//		MySQL 支持 Yes / client lib version 5.0.89
 *		//		GD library Yes / bundled (2.0.34 compatible)
 *		//		eAccelerator/Memcache/xcache Yes
 *		//		cURL support enabled
 *		//时间：2010 09 21 02:56:20//
 */

if(!defined('IN')) {
	exit('Access Denied');
}

class template {

	var $templates_dir = './app/t/template/'; 			//模板路径;
	var $templates_cache = './data/template/'; 		//缓存模板路径;
	var $templates_default = './app/t/template/'; 		//默认模板路径;
	var $templates_new = false; 							//设置当次更新;
	var $templates_time = 360;				//过期时间,分钟计算;

	var $templates_charset = 'utf8';
	var $templates_postfix = '.html'; 		//模板后缀;
	var $templates_auto = true;				//自动更新模板;
	var $templates_caching = '.php';			//缓存后缀;
	var $templates_lonlabel = false;			//长标签;
	var $templates_space = false;			//清除无意义字符
	var $templates_php = false;				//为每个缓存头部增加PHP码;
	var $templates_js = true;				//是否使用JS缓存
	//var $templates_check_set = false;
	
	var $csscurmodules = '';					//当前的模块以便载入对应的CSS缓存
	var $jscurmodules = '';					//当前的模块以便载入对应的JS缓存
	//结果集;
	var $templates_file = array();					//模板文件
	var $templates_cache_file = array();				//缓存文件
	var $templates_xname = '';						//单个模板标识名
	var $templates_sname = '';						//单个子模板标识名
	var $templates_message = '';					//html内容;
	var $templates_update = 0;						//更新次数
	var $templates_assign = array();					//用户用smarty模式;

	var $templates_menu = array();					//用于记录每个模板信息;
	var $templates_menu_new = array();					//用于记录每个模板新信息;
	var $templates_replace = array();				//用于智能更新,可以将模板中的内容直接替换;
	var $templates_debug = array();

	function __construct() {
	}
	
	function set() {
		static $check_set = false;
		if($check_set == true) return;
		$this->path();
		if(is_file($this->templates_cache.'templates_caceh.php') && ($this->templates_auto == true || $this->templates_new == false)) $this->templates_menu = $this->templates_menu_new = include $this->templates_cache.'templates_caceh.php';
		if($this->templates_new == true) $this->templates_menu = array();
		$check_set = true;
	}

	function display($file_name) {
		$this->set();
		//这里是单个模板文件名
		$this->templates_xname = $file_name;
		//取得原始模板地址;
		$this->templates_file[$file_name] = $this->get_path($file_name);
		//取得缓存地址;
		$this->templates_cache_file[$file_name] = $this->get_path();
		
		if($this->check($this->templates_xname) === true) {
			$this->templates_debug['cached'][$this->templates_xname] = true;
			return $this->templates_cache_file[$file_name];
		}
		$this->templates_message = $this->replace_html(file_get_contents($this->templates_file[$file_name], LOCK_EX));
		return	$this->fileplus();
	}

	//生成缓存文件
	private function fileplus() {
		file_put_contents($this->templates_cache_file[$this->templates_xname],$this->templates_message, LOCK_EX);
		$this->templates_message = '';
		return $this->templates_cache_file[$this->templates_xname];
	}

	//控制配置
	function __destruct() {
		if(is_array($this->templates_menu_new) === false) $this->templates_menu_new = array();
		foreach($this->templates_menu_new as $k => $v) {
			if(isset($this->templates_menu[$k]) && $this->templates_menu[$k] == $v) continue;
			else {
				file_put_contents($this->templates_cache.'templates_caceh.php', sprintf("<? if(!defined('IN')) exit('Access Denied');\r\n return %s;",var_export($this->templates_menu_new, true)), LOCK_EX);
				break;
			}
		}
	}

	//控制sub配置，这里因为只在主模板重新生成的时候才生成，所以直接记录时间。
	private function templatessub() {
		$this->templates_menu_new = is_array($this->templates_menu_new) ? $this->templates_menu_new : array();
		if(is_file($this->templates_file[$this->templates_sname]) == true) {
			$this->templates_menu_new[$this->templates_sname] = filemtime($this->templates_file[$this->templates_sname]);
			$this->templates_menu_new[$this->templates_xname.'%sub'][] = $this->templates_sname;
			$this->templates_menu_new[$this->templates_xname.'%sub'] = array_unique($this->templates_menu_new[$this->templates_xname.'%sub']);
		}
	}

	//返回文件真实路径
	private function get_path($file_name = null) {
		if($file_name != null) {
			$file_all = $this->templates_dir.$file_name.$this->templates_postfix;//这里filename可以是common/header这类
			if(is_file($file_all) === false) {
				$this->error('模板文件找不到！', $file_all);//模板文件找不到
			}
			return $file_all;
		} else {
			$cache_name = strtr($this->templates_xname, array('/' => '_', '\\' => '_'));
			//返回当前缓存文件路径
			return $this->templates_cache.$cache_name.'_cache'.$this->templates_caching;
		}
		return false;
	}

	// 路径规范处理;
	private function path() {
		static $cache_bak, $cache_tem;
		if(!$cache_bak) $cache_bak = $this->templates_cache;
		if(!$cache_tem) $cache_tem = $this->templates_dir;
		$this->templates_dir = realpath(strtr($cache_tem, array('./'=>'/','//'=>'/','\\\\'=>'\\'))).DIRECTORY_SEPARATOR;
		$this->templates_cache = realpath(strtr($cache_bak, array('./'=>'/','//'=>'/','\\\\'=>'\\'))).DIRECTORY_SEPARATOR;
		if(strlen($this->templates_dir) <= 2) $this->error('指定模板路径不存在！');
		if(strlen($this->templates_cache) <= 2) {
			if(!dmkdir(strtr($cache_bak, array('./'=>'/','//'=>'/','\\\\'=>'\\')))) {
				$this->error('模板缓存路径不存在！');
			}
			$this->path();
			unset($cache_bak);
			unset($cache_tem);
		}
		return true;
	}

	//控制判断
	private function check($name) {
		//如果配置数组中该模板未注册，如删掉配置数组文件的情况
		if(!$this->templates_menu[$name]) {
			$this->templates_menu_new[$name] = filemtime($this->templates_file[$name]);
			return false;
		}

		//强制本次更新或模板不存在的情况
		if(is_file($this->templates_cache_file[$name]) === false || $this->templates_new == true) {
			return false;
		}

		//如果自动更新和强制更新均关闭，则返回缓存文件
		if($this->templates_auto == false) return true;

		//检测缓存模板的修改时间，这里进行自动更新判断。
		if(TIMESTAMP - filemtime($this->templates_cache_file[$name]) >= $this->templates_time * 60) {
			return false;
		}

		//检测模板的修改
		$template_time = filemtime($this->templates_file[$name]);
		if($template_time != $this->templates_menu[$name]) {
			$this->templates_menu_new[$name] = $template_time;
			return false;
		}
		//在这里要增加对这个模板的sub模块的检测，能够检测到这里，这个数组里的模块肯定多少都没变化，只是修改时间可能不同。这个数组里包含了这个子模块的列表。
		$template_sub_arr = array();
		if(isset($this->templates_menu[$name.'%sub'])) {
			$template_sub_arr = $this->templates_menu[$name.'%sub'];
			foreach($template_sub_arr as $k => $v) {
				if(filemtime($this->get_path($v)) != $this->templates_menu[$v]) return false;
			}
		}
		return true;
	}

	//假如用户用强制性;
	function assign($phpnew_var, $phpnew_value=null) {
		$php_key = $php_val = null;
		if($phpnew_var=='')
		return false;
		if(is_array($phpnew_var)===true){
			foreach ($phpnew_var as $php_key => $php_val){
				if(is_array($php_val)===true){
					$this->assign($php_val);
				}
				if(is_numeric($php_key)===false || $php_key{0}!='_')
				$this->templates_assign[$php_key] = $php_val;
			}
		} else {
			if(is_numeric($phpnew_var)===false || $phpnew_var{0}!='_')
			$this->templates_assign[$phpnew_var] = $phpnew_value;
		}
	}

	//替换函数;
	protected function replace_html($template) {
		//过滤 <!--{}-->
		$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
		//替换,那个被替换的方法在赋值的时候就再次运行了
		$template = preg_replace(
		"/[\n\r\t]*\{templatesub\s+(.+)\}[\n\r\t]*/iseU",
		"\$this->get_contents('$1')",
		$template
		);
		//直接输出php
		$template = preg_replace('/[\n\r\t]*\<\?php.*\\?\>[\n\r\t]*/iseU',
		'\'[php]\'.base64_encode(\'$0\').\'[/php]\'',
		$template
		);

		//静态替换
		$template = preg_replace(
		"/[\n\r\t]*\{html\s+(.+?)\}[\n\r\t]*/ise",
		"\$this->get_var('$1')",
		$template
		);
		//替换模板载入命令
		$template = preg_replace(
		"/[\n\r\t]*\{template\s+\\$(.+)\}[\n\r\t]*/isU",
		'<? include template(-%-$1); ?>',
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{template\s+(.+)\}[\n\r\t]*/isU",
		"<? include template('$1'); ?>",
		$template
		);
		//CSS判断语法，载入的默认的CSS缓存地址
		$template = preg_replace(
		"/[\n\r\t]*\{csstemplate\}[\n\r\t]*/ies", "\$this->loadcsstemplate('\\1')", 
		$template
		);
		//JS判断语法，载入的为默认设置的js缓存文件地址
		$template = preg_replace(
		"/[\n\r\t]*\{jstemplate\}[\n\r\t]*/ies", "\$this->loadjstemplate('\\1')", 
		$template
		);

		//替换 PHP 换行符
		$template = str_replace("{LF}", "<?=\"\\n\"?>", $template);
		//替换直接变量输出
		$varRegexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)"
		. "(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)";
		$template = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?=\\1?>", $template);
		$template = preg_replace("/$varRegexp/es", "\$this->addquote('<?=\\1?>')", $template);
		$template = preg_replace("/\<\?\=\<\?\=$varRegexp\?\>\?\>/es", "\$this->addquote('<?=\\1?>')", $template);
		//替换特定函数
		$template = preg_replace(
		"/[\n\r\t]*\{eval\s+(.+?)\}[\n\r\t]*/ies",
		"\$this->stripvtags('<? \\1; ?>','')",
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/ies",
		"\$this->stripvtags('<? \\1; ?>','')",
		$template
		);
		$template = preg_replace(
		"/([\n\r\t]*)\{elseif\s+(.+?)\}([\n\r\t]*)/ies",
		"\$this->stripvtags('\\1<? } elseif(\\2) { ?>\\3','')",
		$template
		);
		$template = preg_replace(
		"/([\n\r\t]*)\{else\}([\n\r\t]*)/is",
		"\\1<? } else { ?>\\2",
		$template
		);
		//替换循环函数及条件判断语句
		for ($i = 0; $i < 6; $i++) {
			$template = preg_replace(
			"/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\}[\n\r]*(.+?)[\n\r]*\{\/loop\}[\n\r\t]*/ies",
			"\$this->stripvtags('<? if(is_array(\\1)) { foreach(\\1 as \\2) { ?>','\\3<? } } ?>')",
			$template
			);
			$template = preg_replace(
			"/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}[\n\r\t]*(.+?)[\n\r\t]*\{\/loop\}[\n\r\t]*/ies",
			"\$this->stripvtags('<? if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>','\\4<? } } ?>')",
			$template
			);
			$template = preg_replace(
			"/([\n\r\t]*)\{if\s+(.+?)\}([\n\r]*)(.+?)([\n\r]*)\{\/if\}([\n\r\t]*)/ies",
			"\$this->stripvtags('\\1<? if(\\2) { ?>\\3','\\4\\5<? } ?>\\6')",
			$template
			);
		}
		//常量替换
		$template = preg_replace(
		"/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/s",
		"<?=\\1?>",
		$template
		);
		//删除 PHP 代码断间多余的空格及换行
		$template = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $template);

		//其他替换
		$template = preg_replace(
		"/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/e",
		"\$this->transamp('\\0')",
		$template
		);
		$template = preg_replace(
		"/\<script[^\>]*?src=\"(.+?)\".*?\>\s*\<\/script\>/ise",
		"\$this->stripscriptamp('\\1')",
		$template
		);
		$template = preg_replace(
		"/[\n\r\t]*\{block\s+([a-zA-Z0-9_]+)\}(.+?)\{\/block\}/ies",
		"\$this->stripblock('\\1', '\\2')",
		$template
		);
		$template = preg_replace('/[\n\r\t]*\[php\](.*)\[\/php\][\n\r\t]*/iseU', 'base64_decode(\'\\1\')', $template);
		$template = preg_replace('/<script.+script>/iseU', "\$this->replace_js('$0')", $template);
		$template = stripslashes($template);//这个很奇怪
		$template = preg_replace('/<script.+script>/iseU', '\'[script]\'.base64_encode(\'$0\').\'[/script]\'', $template);
		if ($this->templates_space == true) {
			$template = preg_replace(
			array('/\r\n/isU','/<<<EOF/isU'), array('',"\r\n<<<EOF\r\n"), $template);
		}
		$template = preg_replace('/[\n\r\t]*\[script\](.+)\[\/script\][\n\r\t]*/iseU','base64_decode(\'$1\')',$template);
		$template = strtr($template,array('-%-'=>'$','\"'=>'"','<style>'=>'<style type="text/css">','<script>'=>'<script type="text/javascript">'));
		$template = "<? if(!defined('IN')) exit('Access Denied'); ?>\n$template";
		if(empty($this->templates_php) === false) $template = $this->templates_php."\r\n".$template;

		if($this->templates_lonlabel == true) {
			$template = strtr($template,
			array('<?php'=>'<?','<?php echo'=>'<?='));
			$template = strtr($template,
			array('<?'=>'<?php ','<?='=>'<?php echo '));
		}
		$template = strtr($template, $this->templates_replace);
		$this->templates_update +=1;
		return (string) $template;
	}

	//子模板内容返回
	protected function get_contents($filename) {
		$this->templates_file[$filename] = $this->get_path($filename);
		$this->templates_sname = $filename;
		if(is_file($this->templates_file[$filename]) === true) {
			$contents = file_get_contents($this->templates_file[$filename], LOCK_EX);
			$contents = preg_replace(
			"/[\n\r\t]*\{templatesub\s+(.+)\}[\n\r\t]*/iseU",
			"\$this->get_contents('$1')",
			$contents
			);
			$this->templatessub();
			return $contents;
		}
		return false;
	}
	
	protected function get_var($phpnew_name) {
		if(isset($GLOBALS[$phpnew_name]))
		return $GLOBALS[$phpnew_name];
		if(isset($this->templates_assign[$phpnew_name]))
		return $this->templates_assign[$phpnew_name];

		return 'Static variable is not defined';
	}
	
	protected function transamp($template) {
		$template = str_replace('&', '&amp;', $template);
		$template = str_replace('&amp;amp;', '&amp;', $template);
		$template = str_replace('\"', '"', $template);
		return $template;
	}
	
	protected function stripvtags($expr, $statement) {
		$expr = str_replace("\\\"", "\"", preg_replace("/\<\?\=(\\\$.+?)\?\>/s", "\\1", $expr));
		$statement = str_replace("\\\"", "\"", $statement);
		return $expr . $statement;
	}

	protected function addquote($var) {
		return str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $var));
	}

	protected function stripblock($var, $s) {
		$s = str_replace('\\"', '"', $s);
		$s = preg_replace("/<\?=\\\$(.+?)\?>/", "{\$\\1}", $s);
		preg_match_all("/<\?=(.+?)\?>/e", $s, $constary);
		$constadd = '';
		$constary[1] = array_unique($constary[1]);
		foreach($constary[1] as $const) {
			$constadd .= '$__' . $const  .' = ' . $const . ';';
		}
		$s = preg_replace("/<\?=(.+?)\?>/", "{\$__\\1}", $s);
		$s = str_replace('?>', "\n\$$var .= <<<EOF\n", $s);
		$s = str_replace('<?', "\nEOF;\n", $s);
		return "<?php\n$constadd\$$var = <<<EOF" . $s . "\nEOF;\n?>";
	}

	function loadcsstemplate() {
		global $_G;
		$scriptcss = '<link rel="stylesheet" type="text/css" href="<?=URL?>/app/'.CURMODULE.'/template/common/common.css" />';
		return $scriptcss;
	}
	
	function cssvtags($param, $content) {
		global $_G;
		$modules = explode(',', $param);
		foreach($modules as $module) {
			$module .= '::'; //fix notice
			list($b, $m) = explode('::', $module);
			if($b && $b == $_G['basescript'] && (!$m || $m == CURMODULE)) {
				$this->csscurmodules .= $content;
				return;
			}
		}
		return;
	}
	
	/* JS 部分方法 */
	protected function stripscriptamp($s) {
		$s = str_replace('&amp;', '&', $s);
		if($this->templates_js) {
			//这里暂时只支持短标签模式
			if(strexists($s, '<?=') && strexists($s, '?>')) {
				$reg = array(
					"/<\?=URL\?>/",
					"/<\?=CURMODULE\?>/",
					"/<\?=CURFUNCTION\?>/",
				);
				$rep = array(
					''.URL.'',
					''.CURMODULE.'',
					''.CURFUNCTION.'',
				);
				$s = preg_replace($reg, $rep, $s);
			}
			$ps = $s;
			$url = parse_url($s);
			if(((strexists(URL, $url['host']) && $url['host']) || !$url['host']) && $url['path']) {
				$staticurl = 'app/'.CURMODULE.'/static/js/';
				if(strexists($url['path'], $staticurl)) $app = CURMODULE;
				else $app = 'common';
				preg_match('/static\/js\/(.+)$/', $url['path'], $match);
				$match = strtr($match[1], array('/' => '__', '\\' => '__', '-' => '_'));
				$jspath = ROOT.'/data/template/'.$app.'/js/';
				if(!dmkdir($jspath)) $this->error("JS缓存路径创建失败！");
				$s = URL.'/data/template/'.$app.'/js/'.$match;
				//只有设置当次更新，才会重新生成JS缓存，所以在有JS新增的时候，要设置强制更新
				if($this->templates_new) {
					$jsdata = file_get_contents($ps);
					$jsdata = $this->replace_js($jsdata);
					file_put_contents($jspath.$match, $jsdata);
				}
			}
		}
		
		return "<script src=\"$s\" type=\"text/javascript\"></script>";
	}
	
	function replace_js($jsdata) {
		$remove = array(
			'/(^|\r|\n)?\/\*.+?\*\/(\r|\n)/is',
			'/\/\/note.+?(\r|\n)/i',
			//'/\/\/.+?(\r|\n)/i',
			'/\/\/debug.+?(\r|\n)/i',
			//'/(^|\r|\n)(\s|\t)+/',
			//'/(\r|\n)/',
		);
		return preg_replace($remove, '', $jsdata);
	}

	function loadjstemplate() {
		global $_G;
		$scriptjs = '<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script><script src="<?=URL?>/static/js/common/common.js"></script>';
		$content = $this->jscurmodules = '';
		
		//
		$scriptjs .= '<script src="<?=URL?>/app/<?=CURMODULE?>/static/js/<?=CURFUNCTION?>.js"></script>';
		return $scriptjs;
	}

	//删除缓存;
	function cache_dele($path = null) {
		if($path === null){
			$path = $this->templates_cache;
		}
		$file_arr = scandir($path);
		foreach ($file_arr as $val) {
			if($val === '.' || $val === '..'){
				continue;
			}
			if(is_dir($path.$val) === true) $this->cache_dele($path.$val.'/');
			if(is_file($path.$val) === true && $val !== 'index.html') unlink($path.$val);
		}
	}
	
	//在这里增加一个错误函数，调用主模块的差错类显示
	function error($message, $tplname) {
		require_once libfile('class/error');
		error::template_error($message, $tplname);
	}
}


?>
